package services

import (
	"context"
	"encoding/json"
	"math/rand"
	"sanrenx.com/xyxf/app/daos"
	"sanrenx.com/xyxf/app/models"
	"sanrenx.com/xyxf/app/services/cache"
	"sanrenx.com/xyxf/pkg/gredis"
	"sanrenx.com/xyxf/pkg/logging"
	"sanrenx.com/xyxf/pkg/orm"
	"strconv"
)

type MusicSrv struct {
}

/**
 * @Description: 根据类型分页获取分类下歌单列表
 * @receiver srv
 * @param offset 起始
 * @param size 数量
 * @param mode 类型 list:歌单;album:专辑;rank:榜单;
 * @param categoryIds  分类ID数组
 * @param exclude 排除方式
 * @param orderBy 排序方式
 * @return list 歌单列表
 * @return err 异常信息
 */
func (srv *MusicSrv) GetPagePlaylistByMode2Category(offset int64, size int64, mode *string, categoryIds []int, exclude bool, orderBy string) (list []*models.Playlist, err error) {
	var (
		cacheSrv cache.MusicCache
		dataIDs  []string
	)

	ctx := context.Background()
	//不同排序缓存key
	listKey := cacheSrv.GetPlaylistZSetKey(mode, categoryIds, exclude, orderBy)
	//热度缓存KEY 需要增加key到被缓存key中 更新热度
	if orderBy == "hot" {
		_ = gredis.ZAdd(ctx, cacheSrv.GetCacheHotKey(cache.Playlist), listKey, float64(rand.Int()))
	}
	//从缓存中获取分页ID
	if dataIDs, err = gredis.ZRevRange(ctx, listKey, offset, offset+size-1); err != nil {
		logging.Error(err)
		return nil, err
	}

	//补救缓存
	if len(dataIDs) < int(size) {
		modelDao := daos.MusicDao{DB: orm.DB}
		cacheCount, _ := gredis.ZCard(ctx, listKey)                                        //缓存中数量
		dbCount, _ := modelDao.GetPlaylistCountByMode2Category(mode, categoryIds, exclude) //数据库中数量
		//不统一需要同步
		if cacheCount < dbCount {
			//获取数据库中数据
			dbIds, columns, err := modelDao.GetPagePlaylistColumnByMode2Category(0, int(dbCount), mode, categoryIds, exclude, orderBy)
			if err != nil {
				return nil, err
			}
			//空数据
			if dbIds == nil || columns == nil {
				return nil, nil
			}
			//补充到数据库
			for _, column := range columns {
				if orderBy == "hot" {
					_ = gredis.ZAdd(ctx, listKey, column.ID, float64(column.Hot))
				} else {
					createdAt, _ := column.CreatedAt.Unix()
					_ = gredis.ZAdd(ctx, listKey, column.ID, float64(createdAt))
				}
			}

			//获取当前分页数组
			if dbCount > offset {
				var end int64
				diff := dbCount - offset
				if diff > 0 {
					if diff > size {
						end = offset + size
					} else {
						end = offset + diff
					}
					//最大可获得
					dataIDs = dbIds[offset:end]
				}
			}
		}
	}

	//根据缓存ID 获取结构体信息
	for _, cacheID := range dataIDs {
		details := new(models.PlaylistDetails)

		ID, _ := strconv.ParseUint(cacheID, 10, 64)
		dataKey := cacheSrv.GetDataKey(ID, cache.Playlist)
		//数据在缓存中，获取数据
		if gredis.Exist(ctx, dataKey) {
			var cacheData string
			cacheData, err = gredis.Get(ctx, dataKey)
			if err == nil {
				err = json.Unmarshal([]byte(cacheData), &details)
			}
		} else {
			//缓存中没找到，刷新缓存获取数据
			details, err = srv.RefreshCachePlaylistById(ID)
		}
		//检查err
		if err != nil || details == nil {
			logging.Error(err)
			continue
		}

		//返回结构体
		list = append(list, &models.Playlist{
			ID:           details.ID,
			Mode:         details.Mode,
			Name:         details.Name,
			IconUrl:      details.IconUrl,
			CategoryID:   details.CategoryID,
			CategoryName: details.CategoryName,
			KeymanID:     details.KeymanID,
			KeymanName:   details.KeymanName,
			Hot:          details.Hot,
			CreatedAt:    details.CreatedAt,
		})
	}

	return list, nil
}

/**
 * @Description: 获取专辑收录音频列表
 * @receiver srv
 * @param id 歌单ID
 * @param offset 起始
 * @param size 数量
 * @return list 音频列表
 * @return err 异常信息
 */
func (srv *MusicSrv) GetPageAudioByPlaylist(id uint64, offset int64, size int64) (list []*models.Audio, err error) {

	var (
		cacheSrv cache.MusicCache
		dataIDs  []string
	)

	//缓存key
	listKey := cacheSrv.GetPlaylistKey(id)
	ctx := context.Background()
	//热度更新列表
	_ = gredis.ZAdd(ctx, cacheSrv.GetCacheHotKey(cache.Audio), listKey, float64(rand.Int()))
	//从缓存中获取分页ID
	if dataIDs, err = gredis.ZRevRange(ctx, listKey, offset, offset+size-1); err != nil {
		logging.Error(err)
		return nil, err
	}

	//补救缓存
	if len(dataIDs) < int(size) {
		moduleDao := daos.MusicDao{DB: orm.DB}
		cacheCount, _ := gredis.ZCard(ctx, listKey)         //缓存中数量
		dbCount, _ := moduleDao.GetAudioCountByPlaylist(id) //数据库中数量
		//不统一需要同步
		if cacheCount < dbCount {
			//获取数据库中数据
			dbIds, columns, err := moduleDao.GetPageAudioColumnByPlaylist(0, int(dbCount), id, orm.OrderByHotDesc)
			if err != nil {
				return nil, err
			}

			//补充到缓存
			for _, column := range columns {
				_ = gredis.ZAdd(ctx, listKey, column.ID, float64(column.Hot))
			}

			//获取当前分页数组
			if dbCount > offset {
				var end int64
				diff := dbCount - offset
				if diff > 0 {
					if diff > size {
						end = offset + size
					} else {
						end = offset + diff
					}
					//最大可获得
					dataIDs = dbIds[offset:end]
				}
			}
		}

		//根据缓存ID 获取结构体信息
		for _, cacheID := range dataIDs {
			details := new(models.AudioDetails)

			ID, _ := strconv.ParseUint(cacheID, 10, 64)
			dataKey := cacheSrv.GetDataKey(ID, cache.Audio)
			//数据在缓存中，获取数据
			if gredis.Exist(ctx, dataKey) {
				var cacheData string
				cacheData, err = gredis.Get(ctx, dataKey)
				if err == nil {
					err = json.Unmarshal([]byte(cacheData), &details)
				}
			} else {
				//缓存中没找到，刷新缓存获取数据
				details, err = srv.RefreshCacheAudioById(ID)
			}
			//检查err
			if err != nil || details == nil {
				logging.Error(err)
				continue
			}

			//返回结构体
			list = append(list, &models.Audio{
				ID:         details.ID,
				Name:       details.Name,
				KeymanID:   details.KeymanID,
				KeymanName: details.KeymanName,
				AudioUrl:   details.AudioUrl,
				Duration:   details.Duration,
				Hot:        details.Hot,
				CreatedAt:  details.CreatedAt,
			})
		}
	}

	return list, nil
}

/**
 * @Description: 获取人物所有音频列表
 * @receiver srv
 * @param id 歌单ID
 * @param offset 起始
 * @param size 数量
 * @return list 音频列表
 * @return err 异常信息
 */
func (srv *MusicSrv) GetPageAudioByKeyman(offset int64, size int64, keymanIds []int, exclude bool, orderBy string) (list []*models.Audio, err error) {

	var (
		cacheSrv cache.MusicCache
		dataIDs  []string
	)

	//缓存key
	listKey := cacheSrv.GetAudioZSetKey(keymanIds, exclude, orderBy)
	ctx := context.Background()
	//热度更新列表
	_ = gredis.ZAdd(ctx, cacheSrv.GetCacheHotKey(cache.Audio), listKey, float64(rand.Int()))
	//从缓存中获取分页ID
	if dataIDs, err = gredis.ZRevRange(ctx, listKey, offset, offset+size-1); err != nil {
		logging.Error(err)
		return nil, err
	}

	//补救缓存
	if len(dataIDs) < int(size) {
		moduleDao := daos.MusicDao{DB: orm.DB}
		cacheCount, _ := gredis.ZCard(ctx, listKey)                       //缓存中数量
		dbCount, _ := moduleDao.GetAudioCountByKeyman(keymanIds, exclude) //数据库中数量
		//不统一需要同步
		if cacheCount < dbCount {
			//获取数据库中数据
			dbIds, columns, err := moduleDao.GetPageAudioColumnByKeyman(0, int(dbCount), keymanIds, exclude, orderBy)
			if err != nil {
				return nil, err
			}

			//补充到缓存
			for _, column := range columns {
				_ = gredis.ZAdd(ctx, listKey, column.ID, float64(column.Hot))
			}

			//获取当前分页数组
			if dbCount > offset {
				var end int64
				diff := dbCount - offset
				if diff > 0 {
					if diff > size {
						end = offset + size
					} else {
						end = offset + diff
					}
					//最大可获得
					dataIDs = dbIds[offset:end]
				}
			}
		}

		//根据缓存ID 获取结构体信息
		for _, cacheID := range dataIDs {
			details := new(models.AudioDetails)

			ID, _ := strconv.ParseUint(cacheID, 10, 64)
			dataKey := cacheSrv.GetDataKey(ID, cache.Audio)
			//数据在缓存中，获取数据
			if gredis.Exist(ctx, dataKey) {
				var cacheData string
				cacheData, err = gredis.Get(ctx, dataKey)
				if err == nil {
					err = json.Unmarshal([]byte(cacheData), &details)
				}
			} else {
				//缓存中没找到，刷新缓存获取数据
				details, err = srv.RefreshCacheAudioById(ID)
			}
			//检查err
			if err != nil || details == nil {
				logging.Error(err)
				continue
			}

			//返回结构体
			list = append(list, &models.Audio{
				ID:         details.ID,
				Name:       details.Name,
				KeymanID:   details.KeymanID,
				KeymanName: details.KeymanName,
				AudioUrl:   details.AudioUrl,
				Duration:   details.Duration,
				Hot:        details.Hot,
				CreatedAt:  details.CreatedAt,
			})
		}
	}

	return list, nil
}

/**
 * @Description: 根据ID获取歌单详情信息
 * @receiver srv
 * @param id 歌单ID
 * @return data 歌单详情
 * @return err 异常信息
 */
func (srv *MusicSrv) GetPlaylistById(id uint64) (data *models.PlaylistDetails, err error) {

	var cacheSrv cache.MusicCache
	//查找Redis缓存
	dataKey := cacheSrv.GetDataKey(id, cache.Playlist)
	ctx := context.Background()
	if gredis.Exist(ctx, dataKey) {
		cacheData, err := gredis.Get(ctx, dataKey)
		if err != nil {
			logging.Info(err)
		} else {
			_ = json.Unmarshal([]byte(cacheData), &data)
			return data, nil
		}
	}

	//刷新缓存，同时获取数据
	if data, err = srv.RefreshCachePlaylistById(id); err != nil {
		return nil, err
	}

	return data, nil
}

/**
 * @Description: 根据ID获取音频详情信息
 * @receiver srv
 * @param id 音频ID
 * @return data 音频详情
 * @return err 异常信息
 */
func (srv *MusicSrv) GetAudioById(id uint64) (data *models.AudioDetails, err error) {

	var cacheSrv cache.MusicCache
	//查找Redis缓存
	dataKey := cacheSrv.GetDataKey(id, cache.Audio)
	ctx := context.Background()
	if gredis.Exist(ctx, dataKey) {
		cacheData, err := gredis.Get(ctx, dataKey)
		if err != nil {
			logging.Info(err)
		} else {
			_ = json.Unmarshal([]byte(cacheData), &data)
			return data, nil
		}
	}

	//刷新缓存，同时获取数据
	if data, err = srv.RefreshCacheAudioById(id); err != nil {
		return nil, err
	}

	return data, nil
}

/**
 * @Description: 刷新歌单缓存数据
 * @receiver srv
 * @param id 歌单ID
 * @return data 歌单
 * @return err 异常信息
 */
func (srv *MusicSrv) RefreshCachePlaylistById(id uint64) (data *models.PlaylistDetails, err error) {

	modelDao := daos.MusicDao{DB: orm.DB}
	//根据ID查找数据库获取
	if data, err = modelDao.GetPlaylist(id); err != nil {
		logging.Error(err)
		return nil, err
	}

	//更新缓存
	var cacheSrv cache.MusicCache
	dataKey := cacheSrv.GetDataKey(id, cache.Playlist)
	//写一个String缓存, 过期时间1小时
	ctx := context.Background()
	_ = gredis.Set(ctx, dataKey, data, cache.MusicExpireTime)
	//更新缓存列表中 HOT 热度分值
	RefreshCacheKeyHot(cacheSrv.GetCacheHotKey(cache.Playlist), data.ID, data.Hot)
	return data, nil
}

/**
 * @Description: 刷新音频缓存数据
 * @receiver srv
 * @param id 音频ID
 * @return data 音频
 * @return err 异常信息
 */
func (srv *MusicSrv) RefreshCacheAudioById(id uint64) (data *models.AudioDetails, err error) {

	modelDao := daos.MusicDao{DB: orm.DB}
	//根据ID查找数据库获取
	if data, err = modelDao.GetAudio(id); err != nil {
		logging.Error(err)
		return nil, err
	}

	//更新缓存
	var cacheSrv cache.MusicCache
	dataKey := cacheSrv.GetDataKey(id, cache.Audio)
	//写一个String缓存, 过期时间1小时
	ctx := context.Background()
	_ = gredis.Set(ctx, dataKey, data, cache.MusicExpireTime)
	//更新缓存列表中 HOT 热度分值
	RefreshCacheKeyHot(cacheSrv.GetCacheHotKey(cache.Audio), data.ID, data.Hot)
	return data, nil
}
